package com.arcaneshift.manager.action.game;

import java.util.ArrayList;
import java.util.List;

import com.arcaneshift.dao.action.game.DeckDao;
import com.arcaneshift.dao.interfaces.IDeckDao;
import com.arcaneshift.manager.interfaces.ICreatureCardManager;
import com.arcaneshift.manager.interfaces.IDeckCardMapManager;
import com.arcaneshift.manager.interfaces.IDeckManager;
import com.arcaneshift.manager.interfaces.ISpellCardManager;
import com.arcaneshift.model.abstracts.AbstractCard;
import com.arcaneshift.model.action.game.Deck;
import com.arcaneshift.model.action.game.DeckCardMap;
import com.arcaneshift.model.util.CardType;
import com.arcaneshift.security.exceptions.DeckException;
import com.common.dao.interfaces.IDao;
import com.common.dao.util.Criteria;
import com.common.dao.util.QueryOperation;
import com.common.manager.abstracts.AbstractManager;
import com.common.security.exceptions.DatabaseException;
import com.common.security.exceptions.ManagerException;

public class DeckManager extends AbstractManager<Deck> implements IDeckManager {

	private IDeckDao deckDao;
	
	public DeckManager() {
		deckDao = new DeckDao();
	}
	
	@Override
	protected IDao<Deck> getDao() {
		return deckDao;
	}
	
	@Override
	public List<Deck> getAll() throws ManagerException {
		return super.getAll();
	}
	
	@Override
	public void save(Deck item) throws ManagerException, DeckException {
		try {
			IDeckCardMapManager mapManager = new DeckCardMapManager();
			DeckCardMap map;
			
			if (exist(item.getName(), item.getPlayerId())) {
				throw new DeckException(DeckException.DECK_EXISTS);
			}
			
			deckDao.save(item);
			Long deckId = getDeck(item.getPlayerId(), item.getName()).getId();

			for (AbstractCard card : item.getCards()) {
				map = new DeckCardMap();
				
				map.setDeckId(deckId);
				map.setCardId(card.getId());
				map.setType(card.getType());
				mapManager.save(map);
			}
		} catch (DatabaseException databaseException) {
			throw new ManagerException(ManagerException.DATABASE_EXCEPTION);
		} catch (DeckException deckException) {
			throw deckException;
		}
	}
	
	@Override
	public void update(Deck item) throws ManagerException {
		super.update(item);
	}
	
	@Override
	public Deck getById(Long id) throws ManagerException {
		IDeckCardMapManager mapManager = new DeckCardMapManager();
		
		Deck deck = super.getById(id);
		deck.setCards(getCards(mapManager.getByDeckId(id)));
		
		return deck;
	}
	
	@Override
	public void delete(Deck item) throws ManagerException {
		super.delete(item);
	}

	/**
	 * 
	 * @param userId
	 * @param name
	 * @return
	 * @throws ManagerException.DATABASE_EXCEPTION
	 * @throws DeckException.DECK_NOT_FOUND
	 * @throws DeckException.MULTIPLE_DECKS_FOUND
	 */
	private Deck getDeck(Long userId, String name) throws ManagerException, DeckException {
		try {
			List<Criteria> criterias = new ArrayList<Criteria>();
			IDeckCardMapManager mapManager = new DeckCardMapManager();
			Criteria userCriteria = new Criteria(Deck.PLAYER_ID, userId, QueryOperation.EQUALS, false);
			Criteria nameCriteria = new Criteria(Deck.NAME, name, QueryOperation.EQUALS, true);
			
			criterias.add(userCriteria);
			criterias.add(nameCriteria);
			
			List<Deck> decks = deckDao.getByCriteria(criterias);
			Deck deck;
			
			if (decks.size() == 0) {
				throw new DeckException(DeckException.DECK_NOT_FOUND);
			} else if (decks.size() == 1) {
				deck = decks.get(0);
			} else {
				throw new DeckException(DeckException.MULTIPLE_DECKS_FOUND);
			}
			
			List<DeckCardMap> cardMaps = mapManager.getByDeckId(deck.getId());
			deck.setCards(getCards(cardMaps));
			
			return deck;
		} catch (DatabaseException databaseException) {
			throw new ManagerException(ManagerException.DATABASE_EXCEPTION);
		}
	}

	@Override
	public boolean exist(String name, Long playerId) throws ManagerException {
		try {
			List<Criteria> criterias = new ArrayList<Criteria>();
			
			criterias.add(new Criteria(Deck.NAME, name, QueryOperation.EQUALS, true));
			criterias.add(new Criteria(Deck.PLAYER_ID, playerId, QueryOperation.EQUALS, false));
			
			List<Deck> decks = deckDao.getByCriteria(criterias);
			return decks.size() > 0;
		} catch (DatabaseException e) {
			throw new ManagerException(ManagerException.DATABASE_EXCEPTION);
		}
	}
	
	private List<AbstractCard> getCards(List<DeckCardMap> cardMaps) throws ManagerException {
		ICreatureCardManager creatureManager = new CreatureCardManager();
		ISpellCardManager spellManager = new SpellCardManager();
		
		List<AbstractCard> cards = new ArrayList<AbstractCard>();
		
		for (int i = 0; i < cardMaps.size(); i++) {
			if (CardType.CREATURE.equals(cardMaps.get(i).getType())) {
				cards.add(creatureManager.getById(cardMaps.get(i).getCardId()));
			} else if (CardType.SPELL.equals(cardMaps.get(i).getType())) {
				cards.add(spellManager.getById(cardMaps.get(i).getCardId()));
			}
		}
		
		return cards;
	}
}
